package Application.Tool;

import Application.AppJoint;
import Application.Controller.HuePenSettingsController;
import Application.Mask.MaskManager;
import javafx.scene.Parent;
import javafx.fxml.FXMLLoader;
import Application.LayerImage.LayerImage;
import javafx.scene.input.MouseEvent;
import Application.Layer.Layer;
import Application.Layer.LayerDelegate;

public class HuePen extends Tool {
    private Parent root;
    private static HuePen ins = new HuePen();

    private LayerImage mask;
    private int size = 32;
    private double angle = 180.0;

    private double[][] cache;

    private double hue2RGB(double v1, double v2, double vH) {
        if(vH < 0) {
            vH += 1;
        }
        if(vH > 1) {
            vH -= 1;
        }
        if(6.0 * vH < 1) {
            return v1 + (v2 - v1) * 6.0 * vH;
        }
        if(2.0 * vH < 1) {
            return v2;
        }
        if(3.0 * vH < 2) {
            return v1 + (v2 - v1) * ((2.0 / 3.0) - vH) * 6.0;
        }

        return v1;
    }

    private HuePen() {
        super();
        // 加载本工具的工具设置
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/HuePenSettings.fxml"));
            root = loader.load();

            AppJoint.getInstance().loadMasksToComboBox(
                    ((HuePenSettingsController)loader.getController()).getCombo()
            );
        } catch(Exception e) {
            e.printStackTrace();
        }

        mask = MaskManager.getInstance().getMaskByName("test");
    }

    public void setSize(int size) {
        this.size = size;
    }

    public void setAngle(double angle) {
        this.angle = angle;
    }

    public void setMask(String maskName) {
        this.mask = MaskManager.getInstance().getMaskByName(maskName);
    }

    @Override public void onMouseDown(MouseEvent e) {
        int width = AppJoint.getInstance().getCanvasWidth();
        int height = AppJoint.getInstance().getCanvasHeight();

        cache = new double[width][height];

        onMouseMove(e);
    }

    @Override public void onMouseMove(MouseEvent e) {
        double pixelx =
                e.getX() / AppJoint.getInstance().getImageView().getFitWidth() * AppJoint.getInstance().getCanvasWidth();
        double pixely =
                e.getY() / AppJoint.getInstance().getImageView().getFitHeight() * AppJoint.getInstance().getCanvasHeight();
        Layer activeLayer = LayerDelegate.getInstance().getLayerById(AppJoint.getInstance().getActiveLayerId());

        for(int i = -size / 2; i < size / 2; i++) {
            for(int j = -size / 2; j < size / 2; j++) {
                // 实际像素位置
                int imgx = (int)pixelx + i;
                int imgy = (int)pixely + j;
                if(imgx >= 0 && imgx < AppJoint.getInstance().getCanvasWidth() && imgy >= 0 && imgy < AppJoint.getInstance().getCanvasHeight()) {
                    // 蒙版像素位置
                    int maskx = (i + size / 2) * MaskManager.MASK_SIZE / size;
                    int masky = (j + size / 2) * MaskManager.MASK_SIZE / size;
                    // 蒙版灰度
                    int gray = (mask.getPixelArgb(maskx, masky) & 255);

                    if(cache[imgx][imgy] < angle) {
                        double strength = Math.min(angle * gray / 255, angle - cache[imgx][imgy]);
                        cache[imgx][imgy] += strength;

                        int oldColor = activeLayer.getArgb(imgx, imgy);
                        int a1 = (oldColor >> 24) & 0xff;
                        double r1 = ((oldColor & 0x00ff0000) >> 16) / 255.0;
                        double g1 = ((oldColor & 0x0000ff00) >> 8) / 255.0;
                        double b1 = (oldColor & 0x000000ff) / 255.0;

                        double maxRGB = Math.max(Math.max(r1, g1), b1);
                        double minRGB = Math.min(Math.min(r1, g1), b1);
                        double L = (maxRGB + minRGB) / 2.0;
                        double S = 0;
                        double H = 0;
                        if(maxRGB == minRGB) {
                            S = 0;
                            H = 0;
                        } else {
                            if(L < 0.5) {
                                S = (maxRGB - minRGB) / (maxRGB + minRGB);
                            } else {
                                S = (maxRGB - minRGB) / (2.0 - maxRGB - minRGB);
                            }

                            if(r1 >= g1 && r1 >= b1) {
                                H = (g1 - b1) / (maxRGB - minRGB);
                            } else if(g1 >= b1 && g1 >= r1) {
                                H = 2.0 + (b1 - r1) / (maxRGB - minRGB);
                            } else {
                                H = 4.0 + (r1 - g1) / (maxRGB - minRGB);
                            }
                            H = H * 60.0;
                            if(H < 0) {
                                H += 360.0;
                            }
                        }

                        H += strength;
                        if(H > 360) {
                            H -= 360;
                        }
                        if(H < 0) {
                            H += 360;
                        }
                        H /= 360.0;

                        double var1, var2;
                        int r2, g2, b2;
                        if(S == 0) {
                            r2 = (int)(L * 255);
                            g2 = (int)(L * 255);
                            b2 = (int)(L * 255);
                        } else {
                            if(L < 0.5) {
                                var2 = L * (1 + S);
                            } else {
                                var2 = (L + S) - (S * L);
                            }
                            var1 = 2.0 * L - var2;

                            r2 = (int)(255 * hue2RGB(var1, var2, H + (1.0 / 3.0)));
                            g2 = (int)(255 * hue2RGB(var1, var2, H));
                            b2 = (int)(255 * hue2RGB(var1, var2, H - (1.0 / 3.0)));
                        }

                        int newColor = (a1 << 24) | (r2 << 16) | (g2 << 8) | b2;

                        activeLayer.setArgb(imgx, imgy, newColor);
                        LayerDelegate.getInstance().refreshSingle(imgx, imgy);
                    }
                }
            }
        }
    }

    @Override public void onMouseUp(MouseEvent e) {

    }

    @Override public Parent getToolSettings() {
        return root;
    }

    public static HuePen getInstance() {
        return ins;
    }
}
